package com.siyoumi.app.modules.fun.service;

import com.siyoumi.app.entity.FunRecord;
import com.siyoumi.app.entity.SysUser;
import com.siyoumi.app.entity.WxUser;
import com.siyoumi.app.modules.user.service.SvcSysUser;
import com.siyoumi.app.service.FunRecordService;
import com.siyoumi.app.service.WxUserService;
import com.siyoumi.component.XApp;
import com.siyoumi.component.XRedisLock;
import com.siyoumi.component.XSpringContext;
import com.siyoumi.component.http.InputData;
import com.siyoumi.component.http.XHttpContext;
import com.siyoumi.exception.EnumSys;
import com.siyoumi.exception.XException;
import com.siyoumi.mybatispuls.JoinWrapperPlus;
import com.siyoumi.util.*;
import com.siyoumi.validator.XValidator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
public class SvcFun {
    static public SvcFun getBean() {
        return XSpringContext.getBean(SvcFun.class);
    }

    static public FunRecordService getApp() {
        return FunRecordService.getBean();
    }


    public FunRecord getEntityByKey(String key) {
        JoinWrapperPlus<FunRecord> query = getQuery(InputData.getIns());
        query.eq("funrec_uix", key);
        return SvcFun.getApp().first(query);
    }

    public JoinWrapperPlus<FunRecord> getQuery(InputData inputData) {
        String openid = inputData.input("openid");
        String idSrc = inputData.input("id_src");
        String dateBegin = inputData.input("date_begin");
        String dateEnd = inputData.input("date_end");
        String abType = inputData.input("ab_type");

        JoinWrapperPlus<FunRecord> query = getApp().join();
        query.eq("funrec_x_id", XHttpContext.getX());

        if (XStr.hasAnyText(openid)) { //用户
            query.eq("funrec_openid", openid);
        }

        if (XStr.hasAnyText(idSrc)) { //来源
            query.eq("funrec_id_src", idSrc);
        }

        if (XStr.hasAnyText(dateBegin) && XStr.hasAnyText(dateEnd)) {
            LocalDateTime b = XDate.parse(dateBegin);
            LocalDateTime e = XDate.parse(dateEnd);
            query.between("funrec_create_date", b, e);
        }

        if (XStr.hasAnyText(abType)) { //+：加、-：减
            if ("-".equals(abType)) {
                //减
                query.lt("funrec_num", 0);
            } else {
                //加
                query.ge("funrec_num", 0);
            }
        }

        return query;
    }

    public JoinWrapperPlus<FunRecord> getQuery(String openid, String idSrc) {
        InputData inputData = InputData.getIns();
        inputData.put("openid", openid);
        inputData.put("id_src", idSrc);

        return getQuery(inputData);
    }

    /**
     * 未过期的积分记录
     *
     * @param openid
     * @param offset
     * @param limit
     */
    public List<FunRecord> listTrue(String openid, int offset, int limit) {
        JoinWrapperPlus<FunRecord> query = getQuery(openid, null);
        query.select("funrec_id", "funrec_num_left");
        query.gt("funrec_expire_date", XDate.now())
                .gt("funrec_num_left", 0)
                .orderByAsc("funrec_expire_date");
        query.last(XSqlStr.limitX(limit, offset));

        return getApp().get(query);
    }


    public FunRecord add(String key, String openid, String idSrc, String appId, Integer num, String desc) {
        return add(key, openid, idSrc, appId, num, desc);
    }

    /**
     * 加减积分
     *
     * @param key
     * @param openid
     * @param idSrc
     * @param appId
     * @param num
     * @param desc
     * @param str00
     * @param str01
     */
    public FunRecord add(String key, String openid, String idSrc, String appId, Integer num, String desc, String str00, String str01) {
        if (num == 0) {
            throw new XException("num为0");
        }

        FunRecord entity = new FunRecord();
        entity.setFunrec_x_id(XHttpContext.getX());
        entity.setFunrec_uix(key);
        entity.setFunrec_openid(openid);
        entity.setFunrec_id_src(idSrc);
        entity.setFunrec_app_id(appId);
        entity.setFunrec_num(num);
        entity.setFunrec_desc(desc);
        if (str00 != null) {
            entity.setFunrec_str_00(str00);
        }
        if (str01 != null) {
            entity.setFunrec_str_01(str01);
        }

        if (num > 0) {
            XLog.info(this.getClass(), "加操作");
            entity.setFunrec_expire_date(XDate.now().plusYears(1L));
            entity.setFunrec_num_left(entity.getFunrec_num());
        } else {
            XLog.info(this.getClass(), "减操作，特殊");
            String lockKey = "fun|" + openid;
            XReturn r = XRedisLock.lockFunc(lockKey, k -> {
                return handleSub(openid, num);
            });
            XValidator.err(r);
        }
        entity.setAutoID(true);
        getApp().save(entity);

        return entity;
    }

    /**
     * 减操作
     *
     * @param openid
     * @param num
     */
    public XReturn handleSub(String openid, Integer num) {
        num = Math.abs(num);
        XLog.debug(this.getClass(), "开始直接减操作：", num);
        XLog.debug(this.getClass(), "一直获取+记录，直接够扣积分为止");

        Integer preFunSum = 0;
        List<FunRecord> preFunList = new ArrayList<>(); //需要扣减的记录
        while (preFunSum <= num) {
            List<FunRecord> listFun = listTrue(openid, preFunList.size(), 10);
            if (listFun.size() <= 0) {
                return XReturn.getR(20153, "积分不足");
            }

            for (FunRecord entityFun : listFun) {
                preFunList.add(entityFun);
                preFunSum += entityFun.getFunrec_num_left();
            }
        }

        Integer leftFun = num;
        XApp.getTransaction().execute(status -> {
            List<String> updateIds = new ArrayList<>();

            //扣减积分逻辑
            for (FunRecord entityFun : preFunList) {
                if (entityFun.getFunrec_num_left() > leftFun) {
                    XLog.debug(this.getClass(), "减一部分：" + leftFun);
                    int updateSubFun = entityFun.getFunrec_num() - leftFun;
                    getApp().update()
                            .eq("funrec_id", entityFun.getKey())
                            .set("funrec_num_left", updateSubFun);
                } else {
                    XLog.debug(this.getClass(), "全减：" + entityFun.getFunrec_num_left());
                    updateIds.add(entityFun.getKey());
                }
            }

            if (updateIds.size() > 0) {
                XLog.debug(this.getClass(), "批量更新减全部");
                getApp().update()
                        .in("funrec_id", updateIds)
                        .set("funrec_num_left", 0);
            }
            return true;
        });


        return XReturn.getR(0);
    }

    /**
     * 我的积分
     *
     * @param uid
     */
    public Long getFun(String uid) {
        SysUser entity = SvcSysUser.getApp().first(uid);
        if (entity == null) {
            return 0L;
        }
        return entity.getUser_fun();
    }

    /**
     * 更新积分
     *
     * @param uid
     */
    public Long updateFun(String uid) {
        JoinWrapperPlus<FunRecord> query = getQuery(uid, null);
        query.gt("funrec_expire_date", XDate.now())
                .gt("funrec_num_left", 0);
        BigDecimal sum = getApp().sum(query, "funrec_num_left");
        long fun = sum.longValue();

        SysUser entityUser = SvcSysUser.getApp().first(uid);
        if (entityUser == null) {
            XValidator.err(EnumSys.ERR_VAL.getR("not find user"));
        }
        SysUser update = new SysUser();
        update.setUser_id(entityUser.getKey());
        update.setUser_fun(fun);
        SvcSysUser.getApp().updateById(update);

        return fun;
    }


    /**
     * 删除积分
     *
     * @param ids
     */
    @Transactional(propagation = Propagation.MANDATORY)
    public XReturn delete(List<String> ids) {
        JoinWrapperPlus<FunRecord> query = getApp().join();
        query.in(FunRecord.tableKey(), ids)
                .select("DISTINCT funrec_openid openid");
        List<Map<String, Object>> listUser = getApp().getMaps(query);

        XLog.debug(this.getClass(), "先删除记录，再更新所有用户积分");
        getApp().delete(ids);

        for (Map<String, Object> mapItem : listUser) {
            String openid = (String) mapItem.get("openid");
            updateFun(openid);
        }

        return XReturn.getR(0);
    }
}
